
<!DOCTYPE html>
<html lang="en-us">
<head>
  
  <meta charset="UTF-8" />
  <meta name="description" content="网络上的两个程序通过一个双向的通信连接实现数据的交换，这个双向链路的一端称为一个socket。" />
  <title>
    什么是SOCKET ？ Socket - TCP 篇 | DenysG的个人博客
  </title>
  
  <meta name="viewport" content="width=device-width,user-scalable=no,maximum-scale=1,initial-scale=1">
  
  <link rel="canonical" href="https://48474.net/post/waht_it_socket/"/>
  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
  
  <link rel="stylesheet" href="/css/sanitize.css">
  <link rel="stylesheet" href="/css/responsive.css">
  <link rel="stylesheet" href="/css/highlight_monokai.css">
  <link rel="stylesheet" href="/css/theme.css">
  <link rel="stylesheet" href="/css/custom.css">
  
  
  <link href="https://48474.net//index.xml" rel="alternate" type="application/rss+xml" title="DenysG的个人博客" />
  <link href="https://48474.net//index.xml" rel="feed" type="application/rss+xml" title="DenysG的个人博客" />

  
  

</head>



<body>
<div class="container">
  
  <header role="banner">
    <div class="row gutters">
      <div id="site-title" class="col span_6">
        <h1><a href="https://48474.net/">DenysG的个人博客</a></h1>
        <h2>我必须承认，幸运喜欢照顾勇敢的人。---- 达尔文</h2>
      </div>
      <div id="social" class="col span_6">
        <ul>
          
          
          <li><a href="https://github.com/cnphpbb/" target="_blank">GitHub</a></li>
          
        </ul>
      </div>
    </div>
  </header>


  
  <main id="single" role="main">
    <div class="article-header">
      <h1>什么是SOCKET ？ Socket - TCP 篇</h1>
      <div class="meta">
        Nov 3, 2020 &nbsp;
        
          #<a href="/tags/tcp/ip">tcp/ip</a>&nbsp;
        
          #<a href="/tags/socket">socket</a>&nbsp;
        
          #<a href="/tags/network">network</a>&nbsp;
        
          #<a href="/tags/solutions">solutions</a>&nbsp;
        
          #<a href="/tags/guide">Guide</a>&nbsp;
        
          #<a href="/tags/linux">linux</a>&nbsp;
        
      </div>
    </div>
    <article>
      <p>近期工作内容经常会提到 <code>Socket</code>, 但是绝大多数人对于这个还是一个很蒙圈的状态，所以从网上找了一堆的资料，简单的将 Socket 做一个详细一些的解释。</p>
<p>Socket 直译是 <code>插座</code> 的意思，一般翻译成 <code>套接字</code>，简单的来说就是网络上的两个程序通过一个双向的通信连接实现数据的交换，这个连接的一端称为一个socket。</p>
<p>在百科中的解释是 <code>Socket本质是编程接口(API)，对TCP/IP的封装</code> ，所以要想搞明白 <code>Socket</code> 就要先弄明白什么是 <code>TCP/IP</code> ，但是要想搞明白 <code>TCP/IP</code> 又要先弄明 <code>网络七层协议</code>，这个路绕的有点大，下面就一个一个搞清楚。</p>
<h2 id="网络七层协议">网络七层协议</h2>
<p>放式系统互联通信参考模型（英语：Open System Interconnection Reference Model，缩写为 OSI），简称为OSI模型（OSI model），就是通常所说的网络七层协议。</p>
<h3 id="第1层-物理层physical-layer">第1层 物理层(Physical Layer)</h3>
<p>在局部局域网上传送帧，它负责管理电脑通信设备和网络媒体之间的互通。包括了针脚、电压、线缆规范、集线器、中继器、网卡、主机适配器等。在这一层，数据的单位称为比特（bit）。属于物理层定义的典型规范代表包括：EIA/TIA RS-232、EIA/TIA RS-449、V.35、RJ-45等。如 RJ-45，就是日常电脑连接的网线的水晶头的规格。</p>
<h3 id="第2层-数据链接层data-link-layer">第2层 数据链接层(Data Link Layer)</h3>
<p>负责网络寻址、错误侦测和改错。将物理层提供的可能出错的物理连接改造成逻辑上无差错的数据链路，并对物理层的原始数据进行数据封装。数据链路层中的数据封装是指：封装的数据信息中，包含了地址段和数据段等。地址段含有点对点发送节点和接收节点的地址（如MAC），控制段用来表示数格连接帧的类型，数据段包含实际要传输的数据。例如以太网、无线局域网（Wi-Fi）等。</p>
<h3 id="第3层-网络层network-layer">第3层 网络层(Network Layer)</h3>
<p>决定数据的路径选择和转寄，将网络表头（NH）加至数据包，以形成分组。网络表头包含了网络数据。例如:互联网协议（IP）等。</p>
<h3 id="第4层-传输层transport-layer">第4层 传输层(Transport Layer)</h3>
<p>把传输表头（TH）加至数据以形成数据包。传输表头包含了所使用的协议等发送信息。例如:传输控制协议义（TCP、UDP）等。</p>
<h3 id="第5层-会话层session-layer">第5层 会话层(Session Layer)</h3>
<p>负责在数据传输中设置和维护电脑网络中两台电脑之间的通信连接。</p>
<h3 id="第6层-表达层presentation-layer">第6层 表达层(Presentation Layer)</h3>
<p>把数据转换为能与接收者的系统格式兼容并适合传输的格式。</p>
<h3 id="第7层-应用层application-layer">第7层 应用层(Application Layer)</h3>
<p>提供为应用软件而设的界面，以设置与另一应用软件之间的通信。例如: HTTP，HTTPS，FTP，TELNET，SSH，SMTP，POP3等。应用层直接和应用程序接口并提供常见的网络应用服务，应用层也向第六层表示层发出请求。</p>
<blockquote>
<p>上述主要内容来自 <a href="https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B">维基百科</a></p>
</blockquote>
<h2 id="tcpip">TCP/IP</h2>
<p>TCP/IP协议族（英语：TCP/IP Protocol Suite，或TCP/IP Protocols），简称TCP/IP，名称源自该协议家族的两个核心协议：TCP（Transmission Control Protocol 传输控制协议）和IP（Internet Protocol 互联网协议）。</p>
<p>TCP/IP参考模型是一个抽象的分层模型，这个模型中，所有的TCP/IP系列网络协议都被归类到4个抽象的”层”中。每一抽象层创建在低一层提供的服务上，并且为高一层提供服务。</p>
<h3 id="网络接口层">网络接口层</h3>
<p>网络接口层实际上并不是因特网协议组中的一部分，但是它是数据包从一个设备的网络层传输到另外一个设备的网络层的方法。大致可以理解成 OSI 中的 物理层 和 数据连接层（虽然不完全一致）。</p>
<h3 id="网络互连层">网络互连层</h3>
<p>TCP/IP协议族中的网络互连层（internet layer）在OSI模型中叫做网络层（network layer）。 正如最初所定义的，网络层解决在一个单一网络上传输数据包的问题。 随着因特网思想的出现，在这个层上添加附加的功能，也就是将数据从源网络传输到目的网络。</p>
<h3 id="传输层">传输层</h3>
<p>传输层负责传送文本数据，主要协议是TCP协议和UDP协议。</p>
<ul>
<li>
<p>TCP 传输控制协议（Transmission Control Protocol）</p>
<p>是一种面向连接的、可靠的、基于字节流的传输层通信协议，由IETF的RFC 793定义。在简化的计算机网络OSI模型中，它完成第四层传输层所指定的功能。</p>
</li>
<li>
<p>UDP 用户数据包协议（User Datagram Protocol）</p>
<p>是一个简单的面向数据报的传输层协议，UDP只提供数据的不可靠传递，它一旦把应用程序发给网络层的数据发送出去，就不保留数据备份（所以UDP有时候也被认为是不可靠的数据报协议）。</p>
</li>
</ul>
<h3 id="应用层">应用层</h3>
<p>该层包括所有和应用程序协同工作，利用基础网络交换应用程序专用的数据的协议。 应用层是大多数普通与网络相关的程序为了通过网络与其他程序通信所使用的层。这个层的处理过程是应用特有的；数据从网络相关的程序以这种应用内部使用的格式进行传送，然后被编码成标准协议的格式。</p>
<p>每一个应用层协议一般都会使用到两个传输层协议之一： 面向连接的TCP传输控制协议和无连接的包传输的UDP用户数据报文协议。</p>
<p>图1: OSI和TCP/IP的关系图</p>
<p><img src="https://imgs.7y2.org/20201103112504.png" alt="OSI和TCP/IP的关系图"></p>
<h2 id="socket-在-tcpip-中的角色">Socket 在 TCP/IP 中的角色</h2>
<p>Socket是应用层与TCP/IP协议族通信的中间软件抽象层，它是一组接口。在设计模式中，Socket其实就是一个门面模式，它把复杂的TCP/IP协议族隐藏在Socket接口后面，对用户来说，一组简单的接口就是全部，让Socket去组织数据，以符合指定的协议。</p>
<p>图2: Socket在TCP/IP中的角色图</p>
<p><img src="https://imgs.7y2.org/20201103112505.png" alt="Socket在TCP/IP中的角色图"></p>
<h2 id="socket通信流程">Socket通信流程</h2>
<p><code>Socket</code> 起源于UNIX，在Unix一切皆文件哲学的思想下，socket是一种”打开—读/写—关闭”模式的实现，服务器和客户端各自维护一个”文件”，在建立连接打开后，可以向自己文件写入内容供对方读取或者读取对方内容，通讯结束时关闭文件。</p>
<p><code>Socket</code> 是 ”打开—读/写—关闭”模式的实现，以使用TCP协议通讯的 <code>Socket</code> 为例，其交互流程大概是这样子的：</p>
<p>图3：Socket交互流程图</p>
<p><img src="https://imgs.7y2.org/20201103112503.png" alt="Socket交互流程图"></p>
<ul>
<li>服务器根据地址类型（ipv4,ipv6）、socket类型、协议创建socket</li>
<li>服务器为socket绑定ip地址和端口号</li>
<li>服务器socket监听端口号请求，随时准备接收客户端发来的连接，这时候服务器的socket并没有被打开</li>
<li>客户端创建socket</li>
<li>客户端打开socket，根据服务器ip地址和端口号试图连接服务器socket</li>
<li>服务器socket接收到客户端socket请求，被动打开，开始接收客户端请求，直到客户端返回连接信息。这时候socket进入阻塞状态，所谓阻塞即接收方法一直到客户端返回连接信息后才返回，开始接收下一个客户端请求。</li>
<li>客户端连接成功，向服务器发送连接状态信息</li>
<li>服务器接收方法返回，连接成功</li>
<li>客户端向socket写入信息</li>
<li>服务器读取信息</li>
<li>客户端关闭</li>
<li>服务器端关闭</li>
</ul>
<h2 id="三次握手和四次分手">三次握手和四次分手</h2>
<p>在TCP/IP协议中，TCP协议通过三次握手建立一个可靠的连接，服务器socket与客户端socket建立连接的部分其实就是靠 TCP 三次握手来实现。而断开连接需要经过四次分手。</p>
<p>图3：TCP/IP协议图</p>
<p><img src="https://imgs.7y2.org/20201103112506.png" alt="TCP/IP协议图"></p>
<ul>
<li>第一次握手：客户端尝试连接服务器，向服务器发送syn包（同步序列编号Synchronize Sequence Numbers），syn=j，客户端进入SYN_SEND状态等待服务器确认。</li>
<li>第二次握手：服务器接收客户端syn包并确认（ack=j+1），同时向客户端发送一个SYN包（syn=k），即SYN+ACK包，此时服务器进入SYN_RECV状态。</li>
<li>第三次握手：第三次握手：客户端收到服务器的SYN+ACK包，向服务器发送确认包ACK(ack=k+1），此包发送完毕，客户端和服务器进入ESTABLISHED状态，完成三次握手。</li>
</ul>
<p>完成了三次握手，客户端和服务器端就可以开始传送数据。以上就是TCP三次握手的总体介绍。通信结束客户端和服务端就断开连接，需要经过四次分手确认。</p>
<ul>
<li>第一次分手：主机1（可以使客户端，也可以是服务器端），设置Sequence Number和Acknowledgment Number，向主机2发送一个FIN报文段；此时，主机1进入FIN_WAIT_1状态；这表示主机1没有数据要发送给主机2了；</li>
<li>第二次分手：主机2收到了主机1发送的FIN报文段，向主机1回一个ACK报文段，Acknowledgment Number为Sequence Number加1；主机1进入FIN_WAIT_2状态；主机2告诉主机1，我“同意”你的关闭请求；</li>
<li>第三次分手：主机2向主机1发送FIN报文段，请求关闭连接，同时主机2进入LAST_ACK状态；</li>
<li>第四次分手：主机1收到主机2发送的FIN报文段，向主机2发送ACK报文段，然后主机1进入TIME_WAIT状态；主机2收到主机1的ACK报文段以后，就关闭连接；此时，主机1等待2MSL后依然没有收到回复，则证明Server端已正常关闭，那好，主机1也可以关闭连接了。</li>
</ul>
<p>可以看到一次tcp请求的建立及关闭至少进行7次通信，这还不包过数据的通信，而UDP不需3次握手和4次分手。</p>
<p>下次我在补充一下 “TCP和UDP的区别”和“Socket-UDP篇”。</p>
<h3 id="参考网址">参考网址</h3>
<ul>
<li><a href="https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B">维基百科</a></li>
<li><a href="https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE">TCP协议</a></li>
</ul>

      
      
      
    </article>
    


  </main>
  
  <nav class="pagination-single">
    
      <span class="previous">&larr; <a href="https://48474.net/post/raspi-toss-log-01/" rel="prev">折腾日志(1) -- 自用树莓派内网穿透搭建GITEA</a></span>
    
    
      <span class="next"><a href="https://48474.net/post/docker-prune/" rel="next">Docker：清理Docker占用的磁盘空间</a> &rarr;</span>
    
  </nav>


  
  <footer role="contentinfo">
    <div style="text-align:center;">
      
      © 2020 135Get_Team. All rights reserved.
    </div>
  </footer>


</div>

<script src="/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>



</body>
</html>

